Содержание

  • 1  Данные и информация
  • 2  Предобработка данных
    • 2.1  Пропуски
    • 2.2  Дубликаты
    • 2.3  Новые столбцы
  • 3  Анализ данных
    • 3.1  Заведения
      • 3.1.1  Посадочные места
      • 3.1.2  Сетевые заведения
      • 3.1.3  Средние рейтинги
      • 3.1.4  Популярные заведения
    • 3.2  Географические данные
      • 3.2.1  Распределение заведений по типу
      • 3.2.2  Средние рейтинги в зависимости от округа
      • 3.2.3  Расположение заведений на карте
      • 3.2.4  Данные об улицах
        • 3.2.4.1  Улицы с одним заведением
    • 3.3  Стоимость заказа
    • 3.4  Часы работы заведений
    • 3.5  Заведения с низким рейтингом
    • 3.6  Вывод
  • 4  Детализация исследования
    • 4.1  Вывод
  • 5  Презентация

Рынок заведений общественного питания Москвы¶

К содержанию

Проведем исследование для инвесторов из фонда «Shut Up and Take My Money». Наша задача проанализировать рынок заведений общественного питания Москвы. Наша цель - Помочь заказчику определиться с открытием заведения общественного питания

Нам доступен датасет с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года. Информация, размещённая в сервисе Яндекс Бизнес, могла быть добавлена пользователями или найдена в общедоступных источниках. Она носит исключительно справочный характер.

Поэтому мы должны провести следующие действия:

  1. Предобработать полученные данные
  2. Проанализировать закономерности для общественных заведений Москвы:
    • какие это заведения
    • их расположение
    • средняя стоимость чека
    • рейтинги
    • популярные заведения
    • время их работы
  3. Составить отчет о заведениях
  4. Расписать рекомендации
  5. Представить презентацию.

Приступим к выполнению задачи.

Подготовка¶

К содержанию

Для начала загрузим и обновим все необходимые для работы и визуализаций библиотеки

In [1]:
pip install matplotlib --upgrade
Requirement already satisfied: matplotlib in /opt/conda/lib/python3.9/site-packages (3.3.4)
Collecting matplotlib
  Downloading matplotlib-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (11.6 MB)
     |████████████████████████████████| 11.6 MB 1.0 MB/s eta 0:00:01
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (0.11.0)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (2.8.1)
Collecting contourpy>=1.0.1
  Downloading contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (300 kB)
     |████████████████████████████████| 300 kB 59.1 MB/s eta 0:00:01
Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (8.4.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (1.4.4)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (21.3)
Requirement already satisfied: numpy<2,>=1.21 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (1.21.1)
Collecting importlib-resources>=3.2.0
  Downloading importlib_resources-6.0.1-py3-none-any.whl (34 kB)
Collecting fonttools>=4.22.0
  Downloading fonttools-4.42.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.5 MB)
     |████████████████████████████████| 4.5 MB 24.7 MB/s eta 0:00:01
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib) (2.4.7)
Requirement already satisfied: zipp>=3.1.0 in /opt/conda/lib/python3.9/site-packages (from importlib-resources>=3.2.0->matplotlib) (3.5.0)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.9/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)
Installing collected packages: importlib-resources, fonttools, contourpy, matplotlib
  Attempting uninstall: matplotlib
    Found existing installation: matplotlib 3.3.4
    Uninstalling matplotlib-3.3.4:
      Successfully uninstalled matplotlib-3.3.4
Successfully installed contourpy-1.1.0 fonttools-4.42.1 importlib-resources-6.0.1 matplotlib-3.8.0
Note: you may need to restart the kernel to use updated packages.
In [2]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from folium import Map, Marker, Choropleth
from folium.plugins import MarkerCluster
import plotly.express as px
import plotly.graph_objects as go
import json

А также зададим общий стиль всем выводимым графикам, чтобы в дальнейшем представить графики в качестве презентации.

In [3]:
sns.set_style(style='ticks')  #Задать общий размер фигур не получилось, все время съезжало форматирование
sns.set_context('talk')  #Через раз съезжает, видимо из-за индивидуальных настроек каждого графика

Данные и информация¶

К содержанию

Загрузим все имеющиеся у нас данные:

  • Датасет moscow_places со всей информацией о Московских общепитах
  • Данные о географическом расположении каждого места и о географических особенностях Москвы - admin_level_geomap
In [4]:
#Воспользуемся методом try-except, чтобы можно было открыть данные с любого устройства
#Для этого необходимо внести соответствующие изменения в переменную path
try:
    moscow_places = pd.read_csv('/datasets/moscow_places.csv')
    with open('/datasets/admin_level_geomap.geojson', 'r') as f:
        geo_json = json.load(f)
except:
    path = 'C://Users//HOME-5//Downloads//'
    moscow_places = pd.read_csv(path + 'moscow_places.csv')
    with open(path + 'admin_level_geomap.geojson', 'r') as f:
        geo_json = json.load(f)
    
#Зададим ограничения на вывод колонок и количество символов - в данном случае "смягчим"
pd.set_option('display.max_columns', None)
pd.options.display.max_colwidth = 100
    
moscow_places.head()
Out[4]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats
0 WoWфли кафе Москва, улица Дыбенко, 7/1 Северный административный округ ежедневно, 10:00–22:00 55.878494 37.478860 5.0 NaN NaN NaN NaN 0 NaN
1 Четыре комнаты ресторан Москва, улица Дыбенко, 36, корп. 1 Северный административный округ ежедневно, 10:00–22:00 55.875801 37.484479 4.5 выше среднего Средний счёт:1500–1600 ₽ 1550.0 NaN 0 4.0
2 Хазри кафе Москва, Клязьминская улица, 15 Северный административный округ пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 55.889146 37.525901 4.6 средние Средний счёт:от 1000 ₽ 1000.0 NaN 0 45.0
3 Dormouse Coffee Shop кофейня Москва, улица Маршала Федоренко, 12 Северный административный округ ежедневно, 09:00–22:00 55.881608 37.488860 5.0 NaN Цена чашки капучино:155–185 ₽ NaN 170.0 0 NaN
4 Иль Марко пиццерия Москва, Правобережная улица, 1Б Северный административный округ ежедневно, 10:00–22:00 55.881166 37.449357 5.0 средние Средний счёт:400–600 ₽ 500.0 NaN 1 148.0

Итак, мы сохранили все данные в датасет. У нас есть следующие колонки:

  • name — название заведения;
  • category — категория заведения, например «кафе», «пиццерия» или «кофейня»;
  • address — адрес заведения;
  • district — административный район, в котором находится заведение, например Центральный административный округ;
  • hours — информация о днях и часах работы;
  • lat — широта географической точки, в которой находится заведение;
  • lng — долгота географической точки, в которой находится заведение;
  • rating — рейтинг заведения по оценкам пользователей в Яндекс Картах (высшая оценка — 5.0);
  • price — категория цен в заведении, например «средние», «ниже среднего», «выше среднего» и так далее;
  • avg_bill — строка, которая хранит среднюю стоимость заказа в виде диапазона, например:
    • «Средний счёт: 1000–1500 ₽»;
    • «Цена чашки капучино: 130–220 ₽»;
    • «Цена бокала пива: 400–600 ₽».
    • и так далее;
  • middle_avg_bill — число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Средний счёт»:
    • Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений.
    • Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число.
    • Если значения нет или оно не начинается с подстроки «Средний счёт», то в столбец ничего не войдёт.
  • middle_coffee_cup — число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»:
    • Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений.
    • Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число.
    • Если значения нет или оно не начинается с подстроки «Цена одной чашки капучино», то в столбец ничего не войдёт.
  • chain — число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут встречаться ошибки):
    • 0 — заведение не является сетевым
    • 1 — заведение является сетевым
  • seats — количество посадочных мест.
In [5]:
print('В датасете представлено {} уникальных заведений'.format(moscow_places['name'].nunique()),
     '\nВсего в датасете {} заведений'.format(moscow_places['name'].count()))
В датасете представлено 5614 уникальных заведений 
Всего в датасете 8406 заведений

Итак, в нашем датасете есть записи о 8406 заведениях общественного питания, из них 5614 уникальных заведений по названию. Возможно в Москве преобладают несетевые заведения, оценим это позже.

In [6]:
moscow_places.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8406 entries, 0 to 8405
Data columns (total 14 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               8406 non-null   object 
 1   category           8406 non-null   object 
 2   address            8406 non-null   object 
 3   district           8406 non-null   object 
 4   hours              7870 non-null   object 
 5   lat                8406 non-null   float64
 6   lng                8406 non-null   float64
 7   rating             8406 non-null   float64
 8   price              3315 non-null   object 
 9   avg_bill           3816 non-null   object 
 10  middle_avg_bill    3149 non-null   float64
 11  middle_coffee_cup  535 non-null    float64
 12  chain              8406 non-null   int64  
 13  seats              4795 non-null   float64
dtypes: float64(6), int64(1), object(7)
memory usage: 919.5+ KB

Из полученной информации о датасете можно сделать следующие выводы:

  • Как и говорилось ранее у нас есть 14 колонок;
  • Всего 8406 записей;
  • Во всех колонках типы данных логичны и соответствуют данным, которые записаны в колонках;
  • Есть пропуски и их много.

Теперь займёмся предобработкой наших данных, чтобы было проще работать.

Предобработка данных¶

К содержанию

Изучим подробнее пропуски в столбцах и исследуем есть ли у нас дубликаты в данных.

Пропуски¶

К содержанию

Начнём с пропусков, выясним:

  • Где пропуски,
  • Как их много,
  • Можем ли мы с ними что-то сделать.
In [7]:
#Сделаем новую таблицу, чтобы удобнее было тонко настраивать график
data_skips = ((moscow_places.isna().mean()*100)
               .to_frame()
               .rename(columns = {0:'space'})
               .query('space > 0')
               .sort_values(by = 'space', ascending = True))
#строим график, который будет визуализировать только пропуски
(data_skips.plot(kind = 'barh',
                 figsize = (19,8),
                  rot = 0, 
                  legend = False,
                  color = '#66CDAA',
                  fontsize = 16)
 .set_title('Количество пропусков в колонках' + "\n", fontsize = 22, color = 'SteelBlue'))
plt.xlabel('Процент пропущенных значений, %', fontsize = 16)
plt.ylabel('Колонки, в котрых есть пропуски', fontsize = 16)
#Чтобы вывести на график проценты пропусков для наглядности
rects = plt.barh(np.arange(len(data_skips.space)),data_skips.space,0)
plt.bar_label(rects,fontsize = 16,fmt = '%.2f%%')
#добавим линию в районе среднего значения пропусков и подпишем
plt.vlines(x = data_skips.space.mean(), ymin = -0.5, ymax = 5.5, ls='--',colors ='red', lw = 3)
plt.text(data_skips.space.mean() + 0.5, 1, 
         f'Среднее значение: {round(data_skips.space.mean(),2)}%',fontsize = 16)
plt.show()

Согласно полученному графику и информации ранее в датасете есть много пропусков, в среднем в колонках около 53.5% пропусков. Это очень много. У нас есть пропуски в 6 из 14 колонках:

  • middle_coffee_cup - сюда попадают значения, в зависимости от значений из колонки avg_bill, однако, и в колонке avg_bill есть много пропусков и не всегда есть строка "Цена одной чашки капучино". Возможно заведение не продаёт кофе, или просто они нигде не указали стоимость чашки кофе. К сожалению, в колонке почти 94% пропусков, а значит удалить строки с пропусками мы не можем. А также нет логичных способов восполнить существующие пропуски.
  • middle_avg_bill - эта колонка также зависит от значений, указанных в колонке avg_bill. И опять же - либо это связано с пропусками в колонке avg_bill, или не встречается строка "Средний счёт". И в этом случае также трудно осуществить логичную замену.
  • price - возможно значения в этой колонке зависят от колонки middle_avg_bill, но скорее всего просто не всегда указываются.
  • avg_bill - содержит 55% пропусков. В колонке содержатся разные значения для разных типов заведений. Предсказать эти значения мы не можем.
  • seats - уже менее 50% пропусков, возможно это заведения, которые работают только навынос или расположены в торговых центрах и не имеют своих "закрепленных" мест, или же просто не указаны владельцами.
  • hours - всего 6% пропусков, меньше всего во всём датасете. Но и здесь мы не можем узнать, как нам решить эту проблему и что является её причиной.

Итак, у нас есть очень много пропусков, которые нам неподвластны, придётся работать с тем, что есть, возможно это не повлияет на наш анализ. К сожалению, это часто происходит с общедоступными данными, которые заполняются в свободой форме любым желающим.

Дубликаты¶

К содержанию

Теперь приступим к поиску дубликатов. Начнем с полных явных дубликатов.

In [8]:
print('В датасете {} полных явных дубликатов'.format(moscow_places.duplicated().sum()))
В датасете 0 полных явных дубликатов

Итак, полных явных дубликатов нет, значит в сервисах Яндекс.Карты и Яндекс.Бизнес есть механизм, позволяющий избавляться от дублирующейся информации.

Теперь проверим, нет ли неполных дубликатов.

In [9]:
#Проверим в связке имя-адрес
print('В датасете {} неполных дубликатов'.format(moscow_places.duplicated(subset=['name',
                                                                          'address']).sum()))
В датасете 0 неполных дубликатов
In [10]:
#Приведём все названия к нижнему регистру и проверим снова
moscow_places['name'] = moscow_places['name'].str.lower()
print('В датасете {} неполных дубликата'.format(moscow_places.duplicated(subset=['name',
                                                                          'address']).sum()))
В датасете 3 неполных дубликата

Посмотрим, что это за дубликаты.

In [11]:
moscow_places.value_counts(subset=['name','address']).head(5)
Out[11]:
name                       address                                     
хлеб да выпечка            Москва, Ярцевская улица, 19                     2
раковарня клешни и хвосты  Москва, проспект Мира, 118                      2
more poke                  Москва, Волоколамское шоссе, 11, стр. 2         2
навруз                     Москва, Новоясеневский проспект, 1Б, корп. 2    1
на углях                   Москва, 5-я Кабельная улица, 8                  1
dtype: int64

У нас есть три неполных дупликата, удалим их, оставив первые записи.

In [12]:
moscow_places = moscow_places.drop_duplicates(subset=['name','address'], keep = 'first')
print('В датасете {} неполных дубликатов'.format(moscow_places.duplicated(subset=['name',
                                                                          'address']).sum()))
В датасете 0 неполных дубликатов
In [13]:
print('В датасете осталось {} уникальных заведений'.format(moscow_places['name'].nunique()),
     '\nВсего в датасете осталось {} заведений'.format(moscow_places['name'].count()))
В датасете осталось 5512 уникальных заведений 
Всего в датасете осталось 8403 заведений

Итак, при приведении названий заведений к нижнему регистру у нас уменьшилось число уникальных заведений, возможно таким образом нам удалось избавиться от неявных дубликатов, или в Москве много мест с похожими названиями, которые отличались регистром.

Всего из датасета мы удалили три строки, а это менее половины процента данных - 0.04%.

К сожалению, мы не можем полностью проверить наличие неявных дубликатов, так как в нашем датасете хранится информация о 5512 уникальных заведениях. А значит будет трудно найти и обработать возможные опечатки и ошибки. Затраты времени на автоматизацию поиска такого рода аномалий в настоящий момент необоснованы.

Поэтому примем, что в наших данных нет неявных дубликатов и приступим к дальнейшей обработке данных.

Новые столбцы¶

К содержанию

Для простоты работы с данными добавим несколько столбцов:

  • street с названиями улиц;
  • is_24/7 с указанием, является ли заведение круглосуточным.

Эти столбцы нам потребуются для дальнейшего анализа.

In [14]:
#Разбиваем значения колонки address по запятым и получаем второе значение, соответствующее улице
moscow_places['street'] = moscow_places['address'].str.split(',').str[1]

#Выберем заведения, которые работают круглосуточно и ежедневно, за исключением строк, 
#где не указано время работы, и присвоим значение True,
#В противном случае - False
moscow_places['is_24/7'] = np.where(moscow_places['hours'].notnull() 
                                    & (moscow_places['hours'].str.contains('круглосуточно') 
                                    & moscow_places['hours'].str.contains('ежедневно')),
                                    True, False)
moscow_places.sample(5)
Out[14]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats street is_24/7
128 калина быстрое питание Москва, Дубнинская улица, 52, стр. 2 Северный административный округ ежедневно, 10:00–20:00 55.896289 37.555286 3.9 низкие Средний счёт:150–300 ₽ 225.0 NaN 1 124.0 Дубнинская улица False
8207 домашний уют столовая Москва, Южнопортовая улица, 22, стр. 1 Юго-Восточный административный округ пн-пт 09:00–18:00; сб,вс 09:00–17:00 55.705403 37.689609 4.1 NaN Средний счёт:280–330 ₽ 305.0 NaN 0 48.0 Южнопортовая улица False
1636 лес бар,паб Москва, улица Верхняя Масловка, 21 Северный административный округ пн-сб 11:30–00:00; вс 12:00–23:30 55.795568 37.559601 4.6 высокие Цена бокала пива:320–460 ₽ NaN NaN 1 20.0 улица Верхняя Масловка False
3064 арена кафе Москва, Крылатская улица, 10 Западный административный округ ежедневно, 09:00–21:00 55.762955 37.433050 4.1 средние Средний счёт:300–500 ₽ 400.0 NaN 0 84.0 Крылатская улица False
4580 тануки кафе Москва, улица Земляной Вал, 26, стр. 1 Центральный административный округ ежедневно, круглосуточно 55.758099 37.657280 4.3 NaN NaN NaN NaN 1 90.0 улица Земляной Вал True

Отлично, два новых столбца добавлены, теперь мы можем приступить к анализу данных.

Анализ данных¶

К содержанию

Теперь приступим к анализу наших данных:

  • Изучим возможные закономерности
  • Получим подробную информацию о представленных в базе заведениях

Заведения¶

К содержанию

Мы уже знаем сколько заведений представлено в данных, теперь узнаем, какие конкретно заведения встречаются в Москве. В нашей базе данных есть колонка category, которая хранит информацию, к какому типу относится то или иное заведение. Проведем исследования количества типов объектов и подробной информации о каждом типе.

In [15]:
#Создадим датафрейм для типов заведений, для каждого типа:
#найдем количество заведений, среднее количество мест, средний рейтинг и являются ли они сетевыми
category_data = (pd.pivot_table(moscow_places, 
                               index='category', 
                               values=['name','seats','rating','chain'], 
                               aggfunc={'name':'count',
                                        'seats':'median',
                                        'rating':'mean',
                                        'chain':'sum'})
                  .sort_values(by='name',ascending=False))
#Добавим недостающие столбцы
category_data['not_chain'] = category_data['name']-category_data['chain']
#Почему-то съехала последовательность столбцов, пришлось насильно поправить
category_data = category_data[['name','rating','seats','chain','not_chain']]
#Округлим рейтинг
category_data['rating'] = category_data['rating'].round(1)
category_data = category_data.reset_index()
category_data
Out[15]:
category name rating seats chain not_chain
0 кафе 2377 4.1 60.0 779 1598
1 ресторан 2042 4.3 86.0 729 1313
2 кофейня 1413 4.3 80.0 720 693
3 бар,паб 764 4.4 82.0 168 596
4 пиццерия 633 4.3 55.0 330 303
5 быстрое питание 603 4.1 65.0 232 371
6 столовая 315 4.2 75.5 88 227
7 булочная 256 4.3 50.0 157 99

У нас всего есть 8 типов заведений:

  • кафе;
  • ресторан;
  • кофейня;
  • бар,паб;
  • пиццерия;
  • быстрое питание;
  • столовая;
  • булочная.

Для начала изучим, как много заведений каждого типа представлено в файле.

In [16]:
plt.figure(figsize=(10,8))
sns.barplot(data = category_data, y='category',x='name', orient='h').set(ylabel='Тип заведения', 
                                                             xlabel='Количество заведений')
plt.title('Количество заведений по типу', fontsize=20, color='SteelBlue')
plt.show();
  • На первом месте по количеству заведений находится кафе, на втором месте - ресторан, на третьем месте - кофейня. В данных категориях встречается не менее 1000 заведений, в среднем их более 1500 тысяч.
  • Примерно одинаковое количество заведений - от 500 до 800 насчитывают категории бар/паб, пиццерия, быстрое питание.
  • Меньше всего в Москве столовых и булочных - каждая из этих категорий насчитывает менее 500 заведений.

Посадочные места¶

К содержанию

Теперь оценим, сколько в среднем посадочных мест приходится на каждый тип заведения.

In [17]:
plt.figure(figsize=(10,8))
sns.barplot(data = category_data.sort_values(by='seats'),
            y='category',x='seats', orient='h').set(ylabel='Тип заведения', xlabel='Количество мест')
plt.title('Среднее количество мест в зависмости от категории', fontsize=20, color='SteelBlue');
  • Больше всего мест в ресторанах - что не удивительно, ведь в ресторан приходят чаще всего, чтобы насладиться едой и компанией, а также в ресторанах проводятся торжества;
  • Затем идут бары и пабы - здесь количество мест обусловлено тем, что здесь часто проводятся какие-то спортивные мероприятия, а также в конце недели в таких местах очень многолюдно;
  • На третьем месте кофейни - в последнее время растет число кофеен, где можно провести время, тематических кофеен. Плюс кофейни часто распологаются в торговых центрах, где общее пространство с посадочными местами.
  • Затем идут столовые - это обычно места с доступной едой, которые распологаются чаще возле мест, где много офисных работников, работников заводов и т.п. Значит здесь должно быть достаточно мест, чтобы удовлетворить потребность работников ближайших компаний/фирм/заводов и т.д.
  • С небольшим разрывом идут заведения быстрого питания. Здесь высокое число посадочных мест может быть связано с тем, что такие заведения распологаются в торговых центрах.
  • Менее 60 посадочных мест в среднем встречается в кафе, пиццериях и булочных. Это обычно небольшие заведения, которые чаще всего работают навынос.

Сетевые заведения¶

К содержанию

Мы отметили, что из 8403 записей у нас есть 5512 заведений с уникальными наименованиями и сделали предположение, что в Москве преобладают несетевые заведения. Настало время проверить эту теорию.

In [18]:
is_chain = pd.DataFrame({'type': moscow_places['chain'].value_counts().index,
                           'total': moscow_places['chain'].value_counts().values})
is_chain['type'] = is_chain['type'].replace({0: 'Несетевые', 1: 'Сетевые'})

plt.figure(figsize=(8,8))
plt.pie(data=is_chain, x='total', autopct='%.0f%%');
plt.legend(labels=is_chain['type'], loc='best')
plt.title('Соотношение сетевых и несетевых заведений', fontsize=20, color='SteelBlue')
plt.show()

Действительно, 62% заведений в Москве не относятся ни к одной сети. Рассмотрим подробнее, среди каких типов заведений чаще встречаются сетевые.

In [19]:
ax = category_data.plot(kind='barh',stacked=True, x='category', y=['not_chain','chain'], xlabel='Тип заведения', figsize=(15,8))
for i in ax.containers:
    ax.bar_label(i,label_type='center')
ax.set_xlabel('Количество заведений')
ax.set_title('Сетевые заведения по типу заведения', fontsize=20, color='SteelBlue')
ax.legend(labels=['Не сетевые','Сетевые']);
  • Булочные занимают первое место среди сетевых заведений, так как их число превышает в 1.5 раза число несетевых заведений. Также среди пиццерий отмечается небольшое преобладание сетей.
  • Чаще всего сетевыми являются кафе, рестораны и кофейни - более 700 заведений;
  • При этом кофейни примерно поровну встречаются как сетевые, так и не сетевые;

Средние рейтинги¶

К содержанию

Оценим, есть ли различия в средних рейтингах в зависимости от типа заведения.

In [20]:
fig, axes = plt.subplots(1, 2, figsize=(15, 5), sharey=True);
plt.figure(figsize=(10,8));
fig.suptitle('Среднее рейтинг заведений в зависмости от категории', fontsize=20, color='SteelBlue');

sns.barplot(ax=axes[0], data = category_data.sort_values(by='rating'),
            y='category',x='rating',orient='h').set(ylabel='Тип заведения', xlabel='Значение рейтинга');
axes[0].set_xlim(0,5);

sns.barplot(ax=axes[1], data = category_data.sort_values(by='rating'),
            y='category',x='rating',orient='h').set(ylabel='',xlabel='Значение рейтинга');
axes[1].set_xlim(4,5);
axes[1].set_xticks(np.arange(4,5, step = 0.1), minor=True);
for i in axes[1].containers:
    axes[1].bar_label(i,)

plt.show();
<Figure size 720x576 with 0 Axes>
Ирина: Наиболее наглядно получилось только так

Значения среднего рейтинга почти не различаются для заведений разных категорий. Средний рейтинг, в основном превышает 4 балла из 5 возможных. Однако, с небольшим отрывом, лидируют всё же бары и пабы.

Популярные заведения¶

К содержанию

Теперь проанализируем наши сетевые заведения. Выведем самые многочисленные сети в Москве и разберем, чем они отличаются, а также что у них общего.

In [21]:
top_chains = (moscow_places.query('chain == 1')
              .pivot_table(index='name',
                           values=['address','category','price'], 
                           aggfunc={'address':'count','category':'first','price':'first'})
              .sort_values(by='address', ascending=False)
              .reset_index())
top_chains = top_chains.head(15)
top_chains
Out[21]:
name address category price
0 шоколадница 120 кофейня средние
1 домино'с пицца 76 пиццерия средние
2 додо пицца 74 пиццерия средние
3 one price coffee 71 кофейня средние
4 яндекс лавка 69 ресторан None
5 cofix 65 кофейня средние
6 prime 50 ресторан низкие
7 хинкальная 44 быстрое питание средние
8 кофепорт 42 кофейня низкие
9 кулинарная лавка братьев караваевых 39 кафе средние
10 теремок 38 ресторан средние
11 чайхана 37 кафе средние
12 cofefest 32 кофейня средние
13 буханка 32 булочная средние
14 му-му 27 кафе средние

Визуализируем полученную таблицу.

In [22]:
plt.figure(figsize=(10,8))
sns.barplot(data=top_chains, x='address', y='name', orient = 'h', palette = 'summer')
plt.xlabel('Количество заведений')
plt.ylabel('Название сети')
plt.title('Топ-15 популярных сетевых заведений', fontsize=20, color='SteelBlue');
  • С огромным отрывом среди прочих сетей лидирует Шоколадница - в Москве насчитывается 120 заведений данной сети - это кофейня, которая кроме кофе также подаёт еду различной кухни, а также предоставляет возможность заказать комбинированные блюда по фиксированной цене.
  • Остальные сети насчитывают менее 80 заведений;
  • После Шоколадницы наиболее популярные сети - Домино'с Пицца, Додо Пицца, One Price Coffee, Яндекс.Лавка и Cofix - они насчитывют от 60 до 80 заведений. Можно заметить, что в основном это пиццерии и кофейни, которые работают в основном на вынос, кроме Яндекс.Лавки;
  • Затем, более 40 заведений насчитывают сети - Prime, Хинкальная и КОФЕПОРТ;
  • Менее 40 заведений у таких сетей как Кулинарная лавка братьев Краваевых, Теремок, Чайхана, CofeFest, Буханка, Му-Му.

Самые популярные сети в Москве насчитывают более 20 заведений по городу, теперь посмотрим, к каким категориям они относятся.

In [23]:
top_category = top_chains.groupby('category')['address'].sum().to_frame()

plt.figure(figsize=(8,8))
plt.pie(data=top_category, x='address',autopct='%.0f%%', colors=['#751A33','#B34233','#D28F33','#D4B95E','#4EA2A2','#1A8693']);
#Чтобы легенда не перекрывала график
plt.legend(bbox_to_anchor = (1,1), labels=top_category.index)
plt.title('Доля заведений среди топ-15 сетей по категории', fontsize=20, color='SteelBlue')
plt.show()

Большую долю среди сетевых заведений занимают кофейни, но скорее всего это связано с тем, что самая популярная сеть Шоколадница, имеющая больше всего торговых точек, является кофейней. Затем примерно одинаковые доли занимают рестораны и пиццерии. После них идут кафе. Ну и самые маленькие доли, менее 10% от всех заведений, занимают сети быстрого питания и булочные.

Теперь посмотрим, какая ценовая политика характерна для этих заведений.

In [24]:
plt.figure(figsize=(8,8))
sns.barplot(data = top_chains.groupby('price')['address'].sum().to_frame().reset_index(),
            x='price',
            y='address').set(xlabel='Уровень цен', 
                             ylabel='Количество заведений')
plt.title('Средний уровень цен в 15 популярных сетях', fontsize=20, color='SteelBlue');

В основном в популяных сетевых заведениях средние цены, реже низкие. Исключение составляет Яндекс.Лавка, но она скорее представляет собой агрегатор, так что нельзя точно установить ценовой диапазон для этой сети.

Посмотрим, что понимается под "средними" ценами.

In [25]:
moscow_places.query('price =="средние"')['middle_avg_bill'].describe()
Out[25]:
count    1668.000000
mean      598.908273
std       294.840114
min       165.000000
25%       350.000000
50%       500.000000
75%       850.000000
max      2150.000000
Name: middle_avg_bill, dtype: float64

Из полученных данных мы можем сделать вывод, что "средними" можно считать цены за средний чек от 350 до 1000 рублей - выше и ниже этого диапазона могут быть выбросы. Но скорее всего значения среднего чека разнятся от типа заведения, а также мы не можем точно сказать как формируются значения столбца price, возможно они указываются самими хозяевами заведений по их усмотрению.

Принимая во внимание указанные поправки будем считать "средними" цены от 350 до 1000 рублей.

Географические данные¶

К содержанию

Теперь рассмотрим, в каких административных районах Москвы - Округах, расположены исследуемые заведения и какие закономерности в зависимости от расположения мы можем рассмотреть.

In [26]:
district_data = pd.pivot_table(moscow_places, 
                               index='district',
                               columns = 'category',
                               values='name', 
                               aggfunc='count')
#общее число заведений в округе
district_data['total'] = district_data.sum(axis=1)
#Средний рейтинг по округам
district_data = district_data.merge(moscow_places.groupby('district')['rating'].median()
                                    .reset_index(),on='district').sort_values(by='total', ascending = False)
district_data
Out[26]:
district бар,паб булочная быстрое питание кафе кофейня пиццерия ресторан столовая total rating
5 Центральный административный округ 364 50 87 464 428 113 670 66 2242 4.4
2 Северный административный округ 68 39 58 235 193 77 188 41 899 4.3
8 Южный административный округ 68 25 85 264 131 73 202 44 892 4.3
3 Северо-Восточный административный округ 62 28 82 269 159 68 182 40 890 4.2
1 Западный административный округ 50 37 62 238 150 71 218 24 850 4.3
0 Восточный административный округ 53 25 71 272 105 72 160 40 798 4.3
6 Юго-Восточный административный округ 38 13 67 282 89 55 145 25 714 4.2
7 Юго-Западный административный округ 38 27 61 238 96 64 168 17 709 4.3
4 Северо-Западный административный округ 23 12 30 115 62 40 109 18 409 4.3

Всего в нашем датасете есть 9 административных округов, нехватает трех:

  • Зеленоградского
  • Новомосковского
  • Троицкого

Несмотря на то, что они юридически считаются частью Москвы, скорее всего мы их не учитываем из-за удаленности от МКАДа, или иных, неизвестных нам причин.

In [27]:
plt.figure(figsize=(19,8))
sns.barplot(data=district_data,
            y='district', 
            x='total', 
            orient='h').set(xlabel='Количество заведений', ylabel='Округ')
plt.title('Распределение заведений по округам', fontsize=20, color = 'SteelBlue')
plt.show()
  • Больше всего заведений расположено в Центральном административном округе - более двух тысяч заведений.
  • В остальных округах встречается не более 900 заведений.
  • Меньше всего заведений в Северо-Западном административном округе - 409.
  • В среднем в каждом районе получается от 500 до 1000 заведений.

Распределение заведений по типу¶

К содержанию

Теперь оценим, какие типы заведений характерны для каждого округа.

In [28]:
#Чтобы подписи были красивые
tick_labels = ['ЦАО','САО','ЮАО','СВАО','ЗАО','ВАО','ЮВАО','ЮЗАО','СЗАО']
district_data[['district',
               'бар,паб',
               'булочная',
               'быстрое питание',
               'кафе','кофейня',
               'пиццерия',
               'ресторан',
               'столовая']].set_index('district').plot.bar(xlabel='Округ',
                                                           figsize=(20,9),
                                                           stacked=True,
                                                           rot = 0)
plt.ylabel('Количество заведений')
#уберем страшные длинные названия из подписей без нагромождения дополнительных столбцов в таблице
plt.xticks(ticks=[0,1,2,3,4,5,6,7,8],labels=tick_labels)
plt.title('Количество заведений в зависимости от типа в разрезе округов Москвы',
          fontsize = 20, color='SteelBlue')
plt.show()
  • Во всех округах отмечается общая тенденция к преобладанию кафе и ресторанов, при чем кафе лидирует по количеству заведений.
  • В некоторых случаях также отмечается большое количество кофеен - например в ЗАО, САО, СВАО и ЮАО.
  • В каждом округе меньше всего столовых и булочных
  • ЦАО стоит рассмотреть отдельно, так как в этом районе вприницпе больше всего заведений. Какие здесь отличия:
    • Первенство занимают рестораны, в этом округе они опережают кафе. Скорее всего это связано с тем, что в центре столицы предпочтение отдается заведениям, где можно спокойно посидеть и насладиться видами и архитектурой. Также это может быть связано с высоким туристическим потоком в данном районе.
    • Кроме кофеен здесь вырываются в лидеры бары и пабы, скорее всего потому, что центр города больше представляет собой развлекательную/прогулочную/офисную часть города и здесь почти нет жилых зданий. Поэтому для питейных заведений здесь меньше ограничений.

Средние рейтинги в зависимости от округа¶

К содержанию

Оценим, как расположение заведений влияет на средний рейтинг. Для этого построим карту Москвы и укажем на ней фоновую картограмму (Хороплет) со средним рейтингом заведений каждого округа.

In [29]:
# создаём карту Москвы
m = Map(location=[55.751244, 37.618423], tiles='Cartodb Positron')
In [30]:
Choropleth(
    geo_data=geo_json,
    data=district_data,
    columns=['district', 'rating'],
    key_on='properties.name',
    fill_color='YlGn',
    fill_opacity=0.8,
    legend_name='Медианный рейтинг заведений по районам',
).add_to(m)

m
Out[30]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Самый высокий рейтинг - выше 4.37 - в Центральном административном округе. Что не удивительно, здесь расположено большое количество заведений общественного питания;
  • Самые низкие средние рейтинги в Юго-Восточном и Северо-Восточном округах - менее 4.23;
  • В остальных районах средний рейтинг составляет от 4.27 до 4.30 баллов.

Расположение заведений на карте¶

К содержанию

Теперь покажем, как на карте распологаются наши заведения. Для этого воспользуемся кластерами.

In [31]:
marker_cluster = MarkerCluster().add_to(m)
def create_clusters(row):
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['rating']}",
    ).add_to(marker_cluster)
    
moscow_places.apply(create_clusters, axis=1)

m
Out[31]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Это замечательный инструмент, который позволяет найти на карте любое заведение, которое мы захотим, однако его практически невозможно корректно отобразить в презентации. К тому же, не всегда корректно можно сделать выводы из полученной информации. Да, мы можем подтвердить, что в ЦАО действительно больше всего заведений, но кластеры не всегда объединяются по административным округам. Они скорее объединяются по близко расположенным маркерам, поэтому данные будут искажены.

Данные об улицах¶

К содержанию

Теперь посмотрим, на каких же улицах города расположено больше всего заведений общепита.

In [32]:
#получим таблицу с улицами в разрезе по заведениям
streets = pd.pivot_table(moscow_places, index='street', columns='category', values='name', aggfunc='count')
#Добавим недостающие столбцы
streets['total'] = streets.sum(axis=1)
streets = streets.merge(moscow_places.groupby('street')['chain'].sum().reset_index(),on='street')
streets = streets.merge(moscow_places.groupby('street')['rating'].median().reset_index(),on='street')
streets.sample(5)
Out[32]:
street бар,паб булочная быстрое питание кафе кофейня пиццерия ресторан столовая total chain rating
260 Большой Овчинниковский переулок NaN NaN NaN 2.0 NaN NaN NaN NaN 2.0 1 4.20
1138 улица Богородский Вал NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 0 4.50
834 Скаковая улица NaN NaN NaN NaN 1.0 NaN NaN 1.0 2.0 0 4.15
704 Осташковский проезд NaN NaN NaN NaN NaN NaN 2.0 NaN 2.0 0 4.30
1141 улица Большая Молчановка NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 0 4.10
In [33]:
#15 первых улиц по общему количеству заведений
top_15_streets = streets.sort_values(by='total', ascending=False).head(15).reset_index(drop=True)
top_15_streets
Out[33]:
street бар,паб булочная быстрое питание кафе кофейня пиццерия ресторан столовая total chain rating
0 проспект Мира 11.0 4.0 21.0 53.0 36.0 11.0 45.0 2.0 183.0 69 4.20
1 Профсоюзная улица 6.0 4.0 15.0 35.0 18.0 15.0 26.0 3.0 122.0 55 4.30
2 проспект Вернадского 7.0 1.0 12.0 25.0 16.0 12.0 33.0 2.0 108.0 58 4.30
3 Ленинский проспект 10.0 3.0 2.0 26.0 23.0 5.0 33.0 5.0 107.0 38 4.30
4 Ленинградский проспект 15.0 4.0 2.0 12.0 25.0 9.0 25.0 3.0 95.0 37 4.30
5 Дмитровское шоссе 6.0 2.0 10.0 23.0 11.0 8.0 24.0 4.0 88.0 40 4.20
6 Каширское шоссе 2.0 NaN 10.0 20.0 16.0 5.0 19.0 5.0 77.0 44 4.20
7 Варшавское шоссе 6.0 NaN 7.0 18.0 14.0 4.0 20.0 7.0 76.0 27 4.20
8 Ленинградское шоссе 5.0 2.0 5.0 13.0 13.0 3.0 26.0 3.0 70.0 31 4.30
9 МКАД 1.0 NaN 9.0 45.0 4.0 NaN 5.0 1.0 65.0 29 4.10
10 Люблинская улица 5.0 NaN 5.0 26.0 11.0 1.0 10.0 2.0 60.0 23 4.30
11 улица Вавилова 2.0 2.0 11.0 15.0 10.0 3.0 12.0 NaN 55.0 24 4.30
12 Кутузовский проспект 2.0 1.0 2.0 14.0 13.0 3.0 16.0 3.0 54.0 23 4.35
13 улица Миклухо-Маклая 3.0 NaN 4.0 21.0 4.0 2.0 15.0 NaN 49.0 16 4.30
14 Пятницкая улица 9.0 3.0 2.0 7.0 6.0 3.0 18.0 NaN 48.0 24 4.40

В топ улиц вошли самые длинные улицы, которые являются магистралями или хордами, соединяющие сразу несколько районов, или даже проходящие через половину Москвы. Также, в топ-15 попал МКАД, который опоясывет всю Москву и является границей округов, или даже проходит через них. Но здесь нет никакой ошибки, ведь МКАД также является улицей и на нем также можно встретить многие заведения общепита, включая сетевые. Ничего удивительного в том, что эти улицы попали в топ-15 по количеству заведений.

In [57]:
top_15_streets[['street',
               'бар,паб',
               'булочная',
               'быстрое питание',
               'кафе','кофейня',
               'пиццерия',
               'ресторан',
               'столовая']].set_index('street').plot.barh(xlabel='Улица', stacked=True, figsize=(20,10))
plt.xlabel('Количество заведений')
plt.title('Количество заведений в зависимости от типа в разрезе популярных улиц Москвы', fontsize = 20, color='SteelBlue')
plt.show()

По количеству заведений на улицах:

  • Больше всего заведений расположено на Проспекте Мира - 183. Данный проспект проходит через ЦАО, в котором самое большое скопление заведений общепита, а также через СВАО.
  • Затем идёт Профсоюзная улица, которая соединяет многие крупные районы ЮЗАО.
  • Меньше всего заведений на улице Миклухо-Маклая и Пятницкой улице - менее 50 заведений.
  • В среднем на этих улицах расположено около 84 заведений.

Теперь рассмотрим, как тип заведения зависит от улицы, на которой оно расположено.

Итак, у нас снова лидируют кафе, за ними идут рестораны, кофейни и в каких-то случаях бары/пабы, а в каких-то - заведения быстрого питания.

  • Кафе преобладают на улицах: проспект Мира, МКАД, Люблинская улица, Профсоюзная улица, улица Миклухо-Маклая, улица Вавилова и Каширское шоссе.
  • Рестораны лидируют на улицах: Проспект Вернадского, Ленинский проспект, Ленинградское шоссе, Пятницкая улица, Дмитровское шоссе, Варшавское шоссе, Кутузовский проспект.
  • На Ленинградском проспекте помимо ресторанов первое место занимают кофейни, а кафе находятся только на третьем месте, уступая барам.
  • Не везде встречаются булочные, так их нет на следующих улицах: Каширское шоссе, Варшавское шоссе, МКАД, Люблинская улица и улица Миклухо-Маклая.
  • Также на МКАДе отсутсвуют пиццерии.
Улицы с одним заведением¶

К содержанию

Проанализируем, сколько у нас улиц, где есть только одно заведение общественного питания.

In [35]:
one_place_street = streets.query('total == 1')
one_place_street
Out[35]:
street бар,паб булочная быстрое питание кафе кофейня пиццерия ресторан столовая total chain rating
0 1-й Автозаводский проезд NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 1 4.2
1 1-й Балтийский переулок NaN NaN NaN NaN NaN NaN 1.0 NaN 1.0 0 4.4
2 1-й Варшавский проезд NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 0 4.0
3 1-й Вешняковский проезд NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 0 3.4
5 1-й Голутвинский переулок 1.0 NaN NaN NaN NaN NaN NaN NaN 1.0 0 4.1
... ... ... ... ... ... ... ... ... ... ... ... ...
1436 улица Шкулёва NaN NaN NaN NaN NaN NaN 1.0 NaN 1.0 0 4.7
1437 улица Шкулёва 4 NaN NaN NaN NaN 1.0 NaN NaN NaN 1.0 1 4.4
1439 улица Шухова NaN NaN NaN NaN NaN NaN 1.0 NaN 1.0 0 4.1
1442 улица Юннатов NaN NaN NaN NaN NaN NaN NaN 1.0 1.0 0 4.7
1447 № 7 NaN NaN NaN 1.0 NaN NaN NaN NaN 1.0 0 4.8

458 rows × 12 columns

В нашем датасете есть 458 улиц, на которых есть только одно заведение общественного питания. Это примерно 10% от всех улиц Москвы, а также примерно 5% от всех заведений. Большинство этих улиц довольно короткие, часть находится в малонаселенных районах, или индустриальных районах, где спрос на заведения общественного питания невысок.

Проанализируем, какого типа это заведения.

In [36]:
plt.figure(figsize=(15,6))
one_place_street[['бар,паб',
               'булочная',
               'быстрое питание',
               'кафе','кофейня',
               'пиццерия',
               'ресторан',
               'столовая']].sum().sort_values().plot.barh(xlabel='Категория заведения')
plt.xlabel('Количество заведений')
plt.title('Типы единственных заведений на улице', fontsize=20, color='SteelBlue')
plt.show()

Больше всего представлено одиночных кафе - их число превышает 150. Затем идут рестораны и кофейни. После них бары и столовые. Меньше всего одиночных булочных. Впринципе паттерн распределения соответствует общей тенденции и никаких особенностей здесь отметить нельзя.

А что же с сетевыми заведениями?

In [37]:
plt.figure(figsize=(8,8))
one_place_street['chain'].value_counts().plot.pie(autopct='%.0f%%', ylabel='', labels=None)
plt.legend(labels=['Несетевые','Сетевые'])
plt.title('Соотношение сетевых и несетевых заведений', fontsize=20, color='SteelBlue')
plt.show()

Как и ожидалось, здесь также преобладают несетевые заведения - 71%.

Стоимость заказа¶

К содержанию

Теперь оценим, как распределяется значение среднего чека в зависимости от района.

In [38]:
district_prices = pd.pivot_table(moscow_places, index='district', values='middle_avg_bill', aggfunc='median').reset_index()
district_prices
Out[38]:
district middle_avg_bill
0 Восточный административный округ 575.0
1 Западный административный округ 1000.0
2 Северный административный округ 650.0
3 Северо-Восточный административный округ 500.0
4 Северо-Западный административный округ 700.0
5 Центральный административный округ 1000.0
6 Юго-Восточный административный округ 450.0
7 Юго-Западный административный округ 600.0
8 Южный административный округ 500.0
In [39]:
#создадим дополнительную карту, чтобы не было нагромождений
m2 = Map(location=[55.751244, 37.618423], tiles='Cartodb Positron')
#И нанесем на нее хороплет
Choropleth(
    geo_data=geo_json,
    data=district_prices,
    columns=['district', 'middle_avg_bill'],
    key_on='properties.name',
    fill_color='OrRd',
    fill_opacity=0.8,
    legend_name='Средний чек заведений по районам',
).add_to(m2)

m2
Out[39]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Судя по данным карты, удаленность от центра Москвы не сильно влияет на стоимость среднего чека. Скорее влияет сам район:

  • Так, например в Центральном и Западном административных округах отмечается самая высокая стоимость среднего чека - от 908 до 1000 рублей. Скорее всего это обосновано тем, что это самые старые (если не считать новые присоединенные территории) и престижные районы;
  • Затем следуют Северо-Западный и Северный административные округа, здесь средний чек составляет от 633 до 725 рублей.
  • В Юго-Западном и Восточном административном округах посещение общепита в среднем обойдётся в 542-633 рубля.
  • Дешевле всего поход в общепит обходится в Южном, Юго-Восточном и Северо-Восточном административных округах - средний чек здесь до 542 рублей.

Часы работы заведений¶

К содержанию

Проанализируем, как чаще всего работают заведения общепита в Москве.

In [40]:
plt.figure(figsize=(8,8))
moscow_places['is_24/7'].value_counts().plot.pie(autopct='%.0f%%', ylabel='', labels=None)
plt.legend(labels=['Не круглосуточные','Круглосуточные'])
plt.title('Доли заведений по времени работы', fontsize=20, color='SteelBlue')
plt.show()

Всего 9% заведений работают круглосуточно, основная часть заведений работает по расписанию. Проанализируем, заведения какого типа готовы работать круглосуточно.

In [41]:
category_times = pd.pivot_table(data=moscow_places, 
                                index='category', 
                                values=['is_24/7','name'],
                                aggfunc={'is_24/7':'sum','name':'count'}).reset_index().sort_values(by='name')
category_times['not_24/7'] = category_times['name']-category_times['is_24/7']

ax = category_times.plot(kind='barh',stacked=True, x='category', 
                         y=['not_24/7','is_24/7'], xlabel='Тип заведения', figsize=(15,8))
for i in ax.containers:
    ax.bar_label(i,label_type='center')
ax.set_xlabel('Количество заведений')
ax.set_title('Круглосуточные заведения по типу заведения', fontsize=20, color='SteelBlue')
ax.legend(labels=['Не круглосуточные','Круглосуточные']);

Чаще всего, круглосуточно готовы работать в кафе, реже всего круглосуточно работают столовые.

На удивление, среди булочных тоже встречаются круглосуточные заведения, даже чаще, чем среди столовых. Это впечатляет, учитывая особенности работы булочных - обычно они объединены с пекарней и продают свежую выпечку, при круглосуточной работе будет затруднительно постоянно выпекать свежую выпечку.

Выведем все круглосуточные заведения на карту, чтобы посмотреть, где они расположены.

In [42]:
moscow_lat, moscow_lng = 55.751244, 37.618423

# создаём карту Москвы
m3 = Map(location=[moscow_lat, moscow_lng], zoom_start=10, tiles="Cartodb Positron")
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m3)

# применяем функцию create_clusters() к каждой строке с круглосуточными заведениями
moscow_places.loc[moscow_places['is_24/7'] == True].apply(create_clusters, axis=1)

# выводим карту
m3
Out[42]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Больше всего круглосуточных заведений расположены в центре города, что ожидаемо, так как там нет ограничений, которые существуют в жилых районах (особенно спальных). Однако, и на переферии города тоже можно утолить голод в любое время суток, просто для этого придётся подольше поискать.

Меньше всего круглосуточных заведений расположено в восточной части города.

Заведения с низким рейтингом¶

К содержанию

Проанализируем, как обстоят дела с заведениями, которые имеют низкий рейтинг. Для начала определим, какой рейтинг стоит считать низким.

In [43]:
moscow_places['rating'].describe()
Out[43]:
count    8403.000000
mean        4.229894
std         0.470426
min         1.000000
25%         4.100000
50%         4.300000
75%         4.400000
max         5.000000
Name: rating, dtype: float64

Большая часть заведений в датасете имеет рейтинг больше 4. За низкий рейтинг примем всё, что меньше 4 и проанализируем закономерности для этих заведений.

In [44]:
low_rating = moscow_places.query('rating < 4')
print('В датасете {} заведений с низким рейтингом'.format(len(low_rating)))
В датасете 1169 заведений с низким рейтингом

Всего 14% заведений в датасете имеют рейтинг меньше 4. Посмотрим, какие это заведения.

In [45]:
plt.figure(figsize=(15,6))
sns.barplot(data=low_rating['category'].value_counts().to_frame().reset_index(),
            y='index', x='category', orient='h').set(ylabel='Тип заведения',
                                         xlabel='Количество заведений')
plt.title('Распределение заведений с низким рейтингом по категориям',
          fontsize=20, color='SteelBlue')
plt.show()

Больше всего заведений с низким рейтингом среди кафе - около половины от всех заведений с рейтингом меньше 4. Меньше всего таких заведений среди булочных. Похоже, что у посетителей более низкие требования к столовым, питейным заведениям, пиццериям и булочным. Этих заведений гораздо меньше среди всех заведений с низким рейтингом.

Теперь оценим, как выглядят средние чеки в таких заведениях и есть ли у них какие-то особенности.

In [46]:
plt.figure(figsize=(15,6))
sns.barplot(data=low_rating.groupby('category')['middle_avg_bill'].median().reset_index().sort_values(by='middle_avg_bill',
                                                                                                     ascending=False), 
            orient='h', y='category', x='middle_avg_bill').set(xlabel='Стоимость среднего чека', 
                                                               ylabel='Тип заведения')
plt.title('Средний чек заведений с низким рейтингом по категориям',
          fontsize=20, color='SteelBlue')
plt.show()

Похоже, что большинство клиентов питейных заведений недовольны стоимостью среднего чека в этих завдениях. Потому что это самые дорогостоящие заведения с низким рейтингом. В остальном серьезных закономерностей здесь выявить не удастся. Мы не знаем что на самом деле послужило причиной таких низких рейтингов. Однако, можно отметить, что поесть в таких заведениях в среднем можно примерно на 500 рублей.

Вывод¶

К содержанию

Проанализировав полученную информацию мы можем сделать следующие выводы:

  1. В Москве преобладают несетевые заведения - 71%
  2. Большая часть заведений работает по расписанию - 91% и не так много круглосуточных заведений
  3. Самая популярная категория заведений - кафе
  4. В Москве очень мало заведений с рейтингом меньше 4 баллов - 14%
  5. Больше всего заведений расположено в Центральном административном районе города
  6. Средний чек в заведениях зачастую не превышает 1000 рублей и цены зависят от района, в котором расположено заведение.
  7. Большим количеством посадочных мест распологают рестораны.

Дополнительно можно выделить следующие интересные особенности:

  • Больше всего заведений расположено на проспекте Мира, так как это одна из самых длинных улиц, а также расположенных в центральном районе города.
  • Среди сетевых заведений больше всего распространены кофейни. И все сетевые заведения стараются держать низкие или средние (до 1000 рублей) цены.
  • Больше всего в Москве круглосуточных кафе
  • Требовательнее всего посетители относятся также к кафе - у этих заведений больше всего низких рейтингов.

Детализация исследования¶

К содержанию

Перед нами стоит задача помочь основателям фонда «Shut Up and Take My Money» открыть кофейню в Москве, которая будет похожа на кофейню «Central Perk» из сериала «Друзья».

Попробуем узнать, возможно ли это сделать, используя открытые данные, которые предоставляют Яндекс Карты и Яндекс Бизнес. Для этого, получим данные о всех кофейнях, которые есть в Москве.

In [47]:
coffeehouse = moscow_places.query('category == "кофейня"')
print('В датасете представлено {} кофеен'.format(len(coffeehouse)))
В датасете представлено 1413 кофеен

Мы обладаем данными о 1413 кофейнях, теперь рассмотрим их особенности.

In [48]:
plt.figure(figsize=(8,8))
coffeehouse['chain'].value_counts().plot.pie(autopct='%.0f%%', ylabel='', labels=None)
plt.legend(labels=['Несетевые','Сетевые'])
plt.title('Соотношение сетевых и несетевых кофеен', fontsize=20, color='SteelBlue')
plt.show()

В Москве работает почти поровну сетевых и несетевых кофеен, однако преобладают несетевые. В первую очередь нас конечно интересуют несетевые кофейни, так как кофейня в сериале "Друзья" не была сетевой. Да и открывать сразу сетевое заведение и ожидать, что оно станет таким же крутым, как в сериале - нецелесообразно. Поэтому рассмотрим только несетевые заведения.

In [49]:
coffeeshops = coffeehouse.query('chain == 0')
print('Исследуем закономерности для {} несетевых кофеен'.format(len(coffeeshops)))
Исследуем закономерности для 693 несетевых кофеен
In [50]:
district_coffee = pd.pivot_table(coffeeshops, 
                                 index='district', 
                                 values=['name','rating','middle_coffee_cup','is_24/7'], 
                                 aggfunc={'name':'count',
                                          'rating':'median',
                                          'middle_coffee_cup':'median',
                                          'is_24/7':'sum'}).reset_index()
district_coffee
Out[50]:
district is_24/7 middle_coffee_cup name rating
0 Восточный административный округ 0 135.0 54 4.40
1 Западный административный округ 1 170.0 57 4.30
2 Северный административный округ 0 165.0 96 4.40
3 Северо-Восточный административный округ 0 162.5 80 4.35
4 Северо-Западный административный округ 0 165.0 28 4.45
5 Центральный административный округ 6 195.0 207 4.40
6 Юго-Восточный административный округ 0 150.0 60 4.35
7 Юго-Западный административный округ 2 190.5 46 4.40
8 Южный административный округ 0 170.0 65 4.40
In [51]:
plt.figure(figsize=(15,6))
sns.barplot(data=district_coffee.sort_values(by='name',ascending=False),
            y='district',
            x='name',
            orient='h').set(xlabel='Количество заведений',ylabel='Округ')
plt.title('Распределение кофеен по административным округам Москвы', fontsize=20, color='SteelBlue')
plt.show()

Больше всего кофеен в ЦАО и САО. Меньше всего кофеен в Северно-Западном административном округе. Конечно, если заказчик не боится конкуренции и хочет создать доступную кофейню, то стоит открывать кофейню ближе к центру столицы. Однако, не стоит забывать, что кофейня "Central Perk" была расположена рядом с парком. Таким образом, стоит рассматривать Северо-Восточный и Юго-Западный административные округа. Так как в них не так много кофеен, как например в ЦАО и очень много парков и скверов. А также достаточно развитая сеть транспорта, так что пункт с доступностью точно будет выполнен.

Теперь оценим, как же работают кофейни в Москве.

In [52]:
plt.figure(figsize=(8,8))
coffeeshops['is_24/7'].value_counts().plot.pie(autopct='%.0f%%', ylabel='', labels=None)
plt.legend(labels=['Не круглосуточные','Круглосуточные'])
plt.title('Доли кофеен по времени работы', fontsize=20, color='SteelBlue')
plt.show()

Большинство кофеен (99%) работает не круглосуточно, интересующая нас кофейня также не работала круглосуточно. К тому же круглосуточная работа накладывает некоторые ограничения и трудности, как работникам, так и хозяевам заведений. Но, если есть желание, сделать что-то более особенное - то можно попробовать создать круглосуточную кофейню.

Теперь перейдем к оценке рейтингов кофеен в зависимости от их расположения.

In [53]:
#создадим дополнительную карту, чтобы не было нагромождений
m4 = Map(location=[55.751244, 37.618423], tiles='Cartodb Positron')
#И нанесем на нее хороплет
Choropleth(
    geo_data=geo_json,
    data=district_coffee,
    columns=['district', 'rating'],
    key_on='properties.name',
    fill_color='YlGn',
    fill_opacity=0.8,
    legend_name='Средний рейтинг кофеен по районам',
).add_to(m4)

m4
Out[53]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Самые высокие рейтинги у кофеен, расположенных в Северо-Западном административном округе. Еще один плюс к выбору этого района, однако придется держать высокую планку, чтобы соревноваться с другими кофейнями в этом районе. Самые низкие рейтинги в Западном административном округе, похоже здесь самые требовательные посетители.

А как расположение кофейни влияет на цену.

In [54]:
m5 = Map(location=[55.751244, 37.618423], tiles='Cartodb Positron')
#И нанесем на нее хороплет
Choropleth(
    geo_data=geo_json,
    data=district_coffee,
    columns=['district', 'middle_coffee_cup'],
    key_on='properties.name',
    fill_color='PuBu',
    fill_opacity=0.8,
    legend_name='Средняя стоимость чашки кофе по районам',
).add_to(m5)

m5
Out[54]:
Make this Notebook Trusted to load map: File -> Trust Notebook

А вот самая дорогая чашка кофе ждет посетителей в Центральном и Юго-Западном административных округах. Дешевле всего выпить кофе в Восточном административном округе. Возможно здесь не так сильно ценят кофе. Да и вообще в восточной части города средня цена чашки кофе не превышает 155 рублей.

Конечно выгоднее открывать кофейню с высокими ценами, а для этого необходимо делать это там, где такая цена будет оправдана. Также, высокая цена чашки кофе может быть связана с высокой ценой аренды помещения в этом районе. И скорее всего в ЦАО так и есть. Однако, в ЮЗАО аренда скорее всего будет значительно ниже. Так что пока ЮЗАО выглядит как наиболее привлекательный район.

Вывод¶

К содержанию

Исходя из небольшого исследования, которое мы провели, можно сделать вывод, что основателям фонда «Shut Up and Take My Money» вполе удастся создать кофейню мечты.

Для того, чтобы открыть такую же кофейню, как "Central Perk", стоит придерживаться следующих идей:

  • Кофейня должна быть недалеко от парка;
  • Кофейня должна быть доступна (как по стоимости, так и по расположению)
  • Кофейня должна быть не сетевой

Согласно тем данным, которые мы имеем:

  • Больше всего для открытия подходят СВАО и ЮЗАО, так как в этих округах больше всего парков, и эти районы достаточно густо населены, а также в них не так много кофеен, с которыми придется соперничать.
  • К тому же, стоимость аренды помещения в них точно будет меньше, чем в ЦАО.
  • Однако, в ЮЗАО можно установить более высокие цены - более 185 рублей в среднем.

Конечно, успех кофейни зависит не только от удачного расположения и финансовых аспектов, сколько от удовлетворенности посетителей. Да, эти рекомендации решают часть проблем, но только от руководства кофейни будет зависеть, станет ли она такая же популярная и успешная, как "Central Perk".

Презентация¶

К содержанию

Открыть презентацию